home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-05-30 | 21.3 KB | 794 lines | [TEXT/ttxt] |
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Grayscale Image
- * Implementation of the Primitive Operations
- *
- * The image is represented as a Pixmap, i.e. a matrix of pixels
- * each of them specifies the gray level at a particular point
- *
- ************************************************************************
- */
-
- #pragma implementation "image.h"
- #include "image.h"
- #include "std.h"
- #include <minmax.h>
- #include <math.h>
-
-
-
- /*
- *------------------------------------------------------------------------
- * Constructors and destructors
- */
-
- void IMAGE::allocate(
- const card no_rows, // No. of rows
- const card no_cols, // No. of cols
- const card depth // No. of bits per pixel
- )
- {
- valid_code = IMAGE_val_code;
-
- assure((ncols=no_cols) > 0, "Zero image width unexpected");
- assure((nrows=no_rows) > 0, "Zero image height unexpected");
- assure((bits_per_pixel=depth) > 0 && depth <= GRAY_MAXBIT,
- "Zero or too large no. of bits per pixel");
-
- name = "";
- npixels = nrows * ncols;
-
- assert( (scanrows = (GRAY **)calloc(nrows,sizeof(GRAY *))) != 0 );
- assert( (pixels = (GRAY *)calloc(npixels,sizeof(GRAY))) != 0 );
-
- register GRAY * pp = pixels;
- register GRAY **sp = scanrows;
- while( pp < pixels + npixels ) // Build the row index
- *sp++ = pp, pp += ncols;
- assert( sp == scanrows + nrows );
- }
-
- // Set a new image name
- void IMAGE::set_name(const char * new_name)
- {
- if( name != 0 && name[0] != '\0' ) // Dispose of the previous image name
- delete name;
-
- if( new_name == 0 || new_name[0] == '\0' )
- name = ""; // Image is anonymous now
- else
- name = new char[strlen(new_name)+1], strcpy(name,new_name);
- }
-
- // Routing constructor module
- IMAGE::IMAGE(const IMAGE_CREATORS_1op op, const IMAGE& prototype)
- {
- switch(op)
- {
- case Expand:
- _expand(prototype);
- break;
-
- case Shrink:
- _shrink(prototype);
- break;
-
- default:
- _error("Operation %d is not yet implemented",op);
- }
- }
-
-
- IMAGE::~IMAGE(void) // Dispose the image struct
- {
- is_valid();
- if( name != 0 && name[0] != '\0' )
- delete name;
-
- delete scanrows;
- delete pixels;
- valid_code = 0;
- }
-
-
- /*
- *------------------------------------------------------------------------
- * Single Image operations
- */
-
- // Clip pixel values to
- // [0,1<<bits_per_pixel-1].
- // A pixel with the value outside that range
- // (i.e. negative or too big) is set to
- // 0 or 1<<bits_per_pixel-1, resp.
- IMAGE& IMAGE::clip_to_intensity_range(void)
- {
- is_valid();
- const int maxval = (1<<bits_per_pixel)-1;
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- for(; cp < (GRAY_SIGNED *)pixels + npixels; cp++)
- if( *cp < 0 )
- *cp = 0;
- else if( *cp > maxval )
- *cp = maxval;
-
- return *this;
- }
-
- // Perform a transformation
- // pixel = |(signed)pixel|
- // on all the pixels of the image
- IMAGE& IMAGE::abs(void)
- {
- is_valid();
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- for(; cp < (GRAY_SIGNED *)pixels + npixels; cp++)
- if( *cp < 0 )
- *cp = -(*cp);
-
- return *this;
- }
-
- // Apply a user-defined action to each pixel
- // while image is efficiently traversed
- // row-by-row
- IMAGE& IMAGE::apply(PixelAction& action)
- {
- is_valid();
- action.nrows = nrows, action.ncols = ncols;
- register GRAY * cp = pixels;
- for(action.row=0; action.row<nrows; action.row++)
- for(action.col=0; action.col<ncols; action.col++)
- action.operation(*cp++);
-
- return *this;
- }
-
- // Perform the histogram equalization
- IMAGE& IMAGE::equalize(const int no_grays)
- {
- is_valid();
- int orig_no_grays = 1 << bits_per_pixel;
- assert( no_grays <= orig_no_grays );
-
- int * const histogram = (int *)calloc(orig_no_grays,sizeof(int));
-
- // Evaluate the histogram of an image
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- for(; cp < (GRAY_SIGNED *)pixels + npixels; cp++)
- {
- if( *cp <= 0 )
- *cp = 0;
- else if( *cp >= orig_no_grays )
- *cp = orig_no_grays-1;
- histogram[*cp]++;
- }
-
- const int pixels_per_bin_optimal = (npixels + no_grays - 1)/no_grays;
- const int gray_shade_subsample_factor =
- (orig_no_grays + no_grays -1)/no_grays;
- register int pixel;
- int new_pixel, new_pixel_old;
- int accumulated = 0;
- int optimal_accumulated = pixels_per_bin_optimal;
- // Mapping from pixels of original
- // image to [new_pixel_old,new_pixel]
- short * const look_up_table = (short *)malloc(orig_no_grays*sizeof(short));
- // (actually, just to the center of
- // this interval)
-
- // Equalizing the histogram
- for(pixel=0,new_pixel=0,new_pixel_old=0; pixel<orig_no_grays; pixel++)
- {
- accumulated += histogram[pixel];
- while( accumulated > optimal_accumulated )
- new_pixel += gray_shade_subsample_factor,
- optimal_accumulated += pixels_per_bin_optimal;
- assert( new_pixel < orig_no_grays );
- look_up_table[pixel] = (new_pixel >
- new_pixel_old+gray_shade_subsample_factor ?
- (new_pixel + new_pixel_old)/2 :
- new_pixel);
- new_pixel_old = new_pixel;
- }
-
- // Update the image according to the LUT
- for(cp = (GRAY_SIGNED *)pixels; cp < (GRAY_SIGNED *)pixels + npixels; cp++)
- *cp = look_up_table[*cp];
-
- free(histogram);
- free(look_up_table);
-
- return *this;
- }
-
- // Compute the 1. norm of the entire image
- // SUM{ |(signed)pixel[i,j]| }
- double IMAGE::norm_1(void) const
- {
- is_valid();
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- double sum = 0;
- for(; cp < (GRAY_SIGNED *)pixels + npixels; )
- sum += ::abs(*cp++);
-
- return sum;
- }
-
- // Compute the square of the 2. norm of
- // the entire image
- // SUM{ |(signed)pixel[i,j]|^2 }
- double IMAGE::norm_2_sqr(void) const
- {
- is_valid();
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- register double sum = 0;
- for(; cp < (GRAY_SIGNED *)pixels + npixels; )
- sum += ::sqr((long int)*cp++);
-
- return sum;
- }
-
- // Compute the infinity norm of the
- // entire image
- // MAX{ |(signed)pixel[i,j]| }
- int IMAGE::norm_inf(void) const
- {
- is_valid();
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- register int maxp = 0;
- for(; cp < (GRAY_SIGNED *)pixels + npixels; )
- maxp = ::max(::abs(*cp++),maxp);
-
- return maxp;
- }
-
- // Find extremum values of image pixels
- Extrema::Extrema(const IMAGE& image)
- : max_pixel(0,0), min_pixel(0,0)
- {
- image.is_valid();
- max_value = min_value = image(0,0);
-
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)image.pixels;
- register int i,j;
- for(i=0; i<image.nrows; i++)
- for(j=0; j<image.ncols; j++, cp++)
- if( *cp > max_value )
- max_value = *cp, max_pixel.row_val = i, max_pixel.col_val = j;
- else if( *cp < min_value )
- min_value = *cp, min_pixel.row_val = i, min_pixel.col_val = j;
- }
-
- // Normalize pixel values to be
- // in range 0..1<<bits_per_pixel-1
- IMAGE& IMAGE::normalize_for_display(void)
- {
- is_valid();
- Extrema extrema(*this);
- message("\nImage_normalization:"
- "\n\tmin pixel value %d, max pixel value %d",
- extrema.min(), extrema.max());
- if( extrema.max() != extrema.min() )
- {
- double factor = (double)((1<<bits_per_pixel)-1) /
- ( extrema.max() - extrema.min() );
- message("\n\tNormalization is as follows: (pixel - %d)*%g\n",
- extrema.min(), factor);
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- for(; cp < (GRAY_SIGNED *)pixels + npixels; cp++)
- *cp = (int)( (*cp - extrema.min()) * factor + 0.5 );
- }
-
- return *this;
- }
-
- // Print some info about the image
- void IMAGE::info(void) const
- {
- message("\nimage %dx%dx%d '%s' ",nrows,ncols,bits_per_pixel,name);
- }
- // Print the image as a table of pixel values
- // (zeros are printed as dots)
- void IMAGE::print(const char * title) const
- {
- is_valid();
- message("\nImage %dx%dx%d '%s' is as follows\n\n",nrows,ncols,
- bits_per_pixel,title);
-
- register int i,j;
- for(i=0; i<nrows; i++)
- {
- for(j=0; j<ncols; j++)
- if( (*this)(i,j) == 0 )
- message(" . ");
- else
- message("%4d ",(GRAY_SIGNED)(*this)(i,j));
- message("\n");
- }
- message("Done\n");
- }
-
- /*
- *------------------------------------------------------------------------
- * Image-scalar operations
- * Check to see if the preedicate "(signed)pixel operation scalar"
- * holds for ALL the pixels of the image
- */
-
- // is "(signed)pixel OP val" true for all
- // pixels?
-
- #define COMPARISON_WITH_SCALAR(OP) \
- \
- bool IMAGE::operator OP (const int val) const \
- { \
- is_valid(); \
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels; \
- for(; cp < (GRAY_SIGNED *)pixels + npixels; ) \
- if( !(*cp++ OP val) ) \
- return false; \
- \
- return true; \
- } \
-
- COMPARISON_WITH_SCALAR(==)
- COMPARISON_WITH_SCALAR(!=)
- COMPARISON_WITH_SCALAR(<)
- COMPARISON_WITH_SCALAR(<=)
- COMPARISON_WITH_SCALAR(>)
- COMPARISON_WITH_SCALAR(>=)
-
- #undef COMPARISON_WITH_SCALAR
-
-
- /*
- *------------------------------------------------------------------------
- * Image-scalar operations
- * Modify every pixel according to the operation
- */
-
- // For every pixel, do `pixel OP value`
- #define COMPUTED_VAL_ASSIGNMENT(OP) \
- \
- IMAGE& IMAGE::operator OP (const int val) \
- { \
- is_valid(); \
- register GRAY * cp = pixels; \
- while( cp < pixels+npixels ) \
- *cp++ OP val; \
- \
- return *this; \
- } \
-
- COMPUTED_VAL_ASSIGNMENT(=)
- COMPUTED_VAL_ASSIGNMENT(+=)
- COMPUTED_VAL_ASSIGNMENT(-=)
- COMPUTED_VAL_ASSIGNMENT(*=)
- COMPUTED_VAL_ASSIGNMENT(|=)
- COMPUTED_VAL_ASSIGNMENT(&=)
- COMPUTED_VAL_ASSIGNMENT(^=)
-
- #undef COMPUTED_VAL_ASSIGNMENT
-
- // Shift the value of all the pixels
- IMAGE& IMAGE::operator <<= (const int val)
- {
- is_valid();
- if( ::abs(val) >= GRAY_MAXBIT )
- _error("Very fishy shift factor: %d",val);
-
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- while( cp < (GRAY_SIGNED *)pixels+npixels )
- *cp++ <<= val;
-
- return *this;
- }
-
- // Shift the value of all the pixels
- IMAGE& IMAGE::operator >>= (const int val)
- {
- is_valid();
- if( ::abs(val) >= GRAY_MAXBIT )
- _error("Very fishy shift factor: %d",val);
-
- register GRAY_SIGNED * cp = (GRAY_SIGNED *)pixels;
- while( cp < (GRAY_SIGNED *)pixels+npixels )
- *cp++ >>= val;
-
- return *this;
- }
-
- /*
- *------------------------------------------------------------------------
- * Image-Image operations
- * Modify the target image according to the operation
- */
-
- IMAGE& IMAGE::operator = (const IMAGE& source)
- {
- are_compatible(*this,source);
- memcpy(pixels,source.pixels,npixels*sizeof(GRAY));
- return *this;
- }
-
- bool operator == (const IMAGE& im1, const IMAGE& im2)
- {
- are_compatible(im1,im2);
- return (memcmp(im1.pixels,im2.pixels,im1.npixels*sizeof(GRAY)) == 0);
- }
-
- // Do "image OP src"
- #define COMPUTED_ASSIGNMENT(OP) \
- \
- IMAGE& IMAGE::operator OP (const IMAGE& src) \
- { \
- are_compatible(*this,src); \
- \
- register GRAY * sp = src.pixels; \
- register GRAY * tp = pixels; \
- while( tp < pixels + npixels ) \
- *tp++ OP *sp++; \
- \
- return *this; \
- } \
-
- COMPUTED_ASSIGNMENT(+=)
- COMPUTED_ASSIGNMENT(-=)
- COMPUTED_ASSIGNMENT(|=)
- COMPUTED_ASSIGNMENT(&=)
- COMPUTED_ASSIGNMENT(^=)
-
- #undef COMPUTED_ASSIGNMENT
-
- #if 0
- // Modified addition
- // Target += scalar*Source
- IMAGE& add(IMAGE& target, const int scalar,const IMAGE& source)
- {
- are_compatible(target,source);
-
- register GRAY * sp = source.pixels;
- register GRAY * tp = target.pixels;
- while( tp < target.pixels + target.npixels )
- *tp++ += scalar * *sp++;
-
- return target;
- }
- // Shift the source to 'pos', clip it
- // if necessary, multiply by the scalar,
- // and add
- IMAGE& IMAGE::shift_clip_add(rowcol pos, const int scalar, const IMAGE& source)
- {
- source.is_valid();
- is_valid();
-
- // Find an intersection of 'source'
- // shifted to 'pos' (relative to
- // the image) with the (target) image
- // Only this rectangle will be
- // affected by addition
- rowcol s_orig(max(0,-pos.row()),max(0,-pos.col()));
- rowcol t_orig(max(0,pos.row()),max(0,pos.col()));
- // No. of rows in the intersection
- const int irows = min(nrows - t_orig.row(),source.nrows - s_orig.row());
- // No. of cols in the intersection
- const int icols = min(ncols - t_orig.col(),source.ncols - s_orig.col());
- if( irows <= 0 || icols <= 0 )
- source.info(),
- info(),
- _error("The first image shifted by (%d,%d) does not intersect the other",
- pos.row(),pos.col());
-
- // See the explanation in the class
- // Rectangle
- const int s_inc_to_nextrow = source.ncols - icols;
- const int t_inc_to_nextrow = ncols - icols;
-
- register GRAY_SIGNED * sp =
- (GRAY_SIGNED *)&(source.scanrows[s_orig.row()])[s_orig.col()];
- register GRAY_SIGNED * tp =
- (GRAY_SIGNED *)&(scanrows[t_orig.row()])[t_orig.col()];
-
- register int i,j;
- for(i=0; i<irows; i++, sp += s_inc_to_nextrow, tp += t_inc_to_nextrow)
- for(j=0; j<icols; j++)
- *tp++ += scalar * *sp++;
-
- return *this;
- }
- #endif
-
- // Evaluate the scalar product of two images
- double operator * (const IMAGE& im1, const IMAGE& im2)
- {
- are_compatible(im1,im2);
-
- register GRAY_SIGNED * p1 = (GRAY_SIGNED *)im1.pixels;
- register GRAY_SIGNED * p2 = (GRAY_SIGNED *)im2.pixels;
- register double sum = 0;
-
- while( p1 < (GRAY_SIGNED *)im1.pixels + im1.npixels )
- sum += *p1++ * *p2++;
-
- return sum;
- }
-
- // Estimate the norm of the difference
- // between two (signed) image
- // SUM{ |(signed)pixel[i,j]| }
- double norm_1(const IMAGE& im1, const IMAGE& im2)
- {
- are_compatible(im1,im2);
-
- register GRAY_SIGNED * p1 = (GRAY_SIGNED *)im1.pixels;
- register GRAY_SIGNED * p2 = (GRAY_SIGNED *)im2.pixels;
- register double sum = 0;
-
- while( p1 < (GRAY_SIGNED *)im1.pixels + im1.npixels )
- sum += ::abs(*p1++ - *p2++);
-
- return sum;
- }
-
- // SUM{ |(signed)pixel[i,j]|^2 }
- double norm_2_sqr(const IMAGE& im1, const IMAGE& im2)
- {
- are_compatible(im1,im2);
-
- register GRAY_SIGNED * p1 = (GRAY_SIGNED *)im1.pixels;
- register GRAY_SIGNED * p2 = (GRAY_SIGNED *)im2.pixels;
- register double sum = 0;
-
- while( p1 < (GRAY_SIGNED *)im1.pixels + im1.npixels )
- sum += ::sqr((long int)(*p1++ - *p2++));
-
- return sum;
- }
- // MAX{ |(signed)pixel[i,j]| }
- int norm_inf(const IMAGE& im1, const IMAGE& im2)
- {
- are_compatible(im1,im2);
-
- register GRAY_SIGNED * p1 = (GRAY_SIGNED *)im1.pixels;
- register GRAY_SIGNED * p2 = (GRAY_SIGNED *)im2.pixels;
- register int maxp = 0;
-
- while( p1 < (GRAY_SIGNED *)im1.pixels + im1.npixels )
- maxp = max(maxp,::abs(*p1++ - *p2++));
-
- return maxp;
- }
-
-
- void compare( // Compare the two images
- const IMAGE& image1, // and print out the result of comparison
- const IMAGE& image2,
- const char * title )
- {
- register int i,j;
-
- are_compatible(image1,image2);
-
- message("\n\nComparison of two images %dx%dx%d\n%s\n",image1.nrows,
- image1.ncols,image1.bits_per_pixel,title);
-
- if( image1.bits_per_pixel != image2.bits_per_pixel )
- message("\nNote, images have different depth, %d and %d\n",
- image1.bits_per_pixel, image2.bits_per_pixel);
-
- double norm1 = 0, norm2 = 0; // Norm of the images
- double ndiff = 0; // Norm of the difference
- int imax=0,jmax=0,difmax = -1; // For the pixels that differ most
- // Image scanline pointer
- register GRAY_SIGNED *rowp1 = (GRAY_SIGNED *)image1.pixels;
- register GRAY_SIGNED *rowp2 = (GRAY_SIGNED *)image2.pixels;
-
- bool im1_neg_pixel = false; // Flags whether negative pixels
- bool im2_neg_pixel = false; // were encountered
-
- for(i=0; i < image1.nrows; i++)
- for(j=0; j < image1.ncols; j++)
- {
- int pv1 = *rowp1++;
- int pv2 = *rowp2++;
- int diff = abs(pv1-pv2);
-
- if( pv1 < 0 )
- im1_neg_pixel = true, pv1 = -pv1;
- if( pv2 < 0 )
- im2_neg_pixel = true, pv2 = -pv2;
-
- if( diff > difmax )
- {
- difmax = diff;
- imax = i;
- jmax = j;
- }
- norm1 += pv1;
- norm2 += pv2;
- ndiff += diff;
- }
-
- if( im1_neg_pixel )
- message("\n*** Warning: a pixel with negative value has been encountered "
- "in image 1\n");
- if( im2_neg_pixel )
- message("\n*** Warning: a pixel with negative value has been encountered "
- "in image 2\n");
-
- message("\nMaximal discrepancy \t\t%d",difmax);
- message("\n occured at the pixel\t\t(%d,%d)",imax,jmax);
- const int pv1 = image1(imax,jmax);
- const int pv2 = image2(imax,jmax);
- message("\n Image 1 pixel is \t\t%d",pv1);
- message("\n Image 2 pixel is \t\t%d",pv2);
- message("\n Absolute error v2[i]-v1[i]\t\t%d",pv2-pv1);
- message("\n Relative error\t\t\t\t%g\n",
- (pv2-pv1)/max((float)abs(pv2+pv1)/2,1e-7) );
-
- message("\nL1 norm of image 1 per pixel \t\t%g",
- (double)norm1/image1.npixels);
- message("\nL1 norm of image 2 per pixel \t\t%g",
- (double)norm2/image2.npixels);
- message("\nL1 norm of image1-image2 per pixel\t\t\t%g",
- (double)ndiff/image1.npixels);
- message("\n||Image1-Image2||/sqrt(||Image1|| ||Image2||)\t%g\n\n",
- ndiff/max( sqrt(norm1*norm2), 1e-7 ) );
-
- }
-
- /*
- *------------------------------------------------------------------------
- * Expansion/Shrinking of the image
- */
-
- // Expand the prototype twice in each
- // dimension
- // In other words, blow each pixel of the
- // prototype to the 2x2 square
- void IMAGE::_expand(const IMAGE& prototype)
- {
- prototype.is_valid();
- allocate(2*prototype.nrows,2*prototype.ncols,prototype.bits_per_pixel);
-
- register GRAY * tp = pixels;
- register GRAY * pp = prototype.pixels;
- register int i,j;
-
- for(i=0; i<prototype.nrows; i++, tp+=ncols)
- for(j=0; j<prototype.ncols; j++)
- {
- register int pixel = *pp++;
- *(tp+ncols) = pixel; // Fill out 2x2 square of the
- *tp++ = pixel; // blown image with a pixel value
- *(tp+ncols) = pixel;
- *tp++ = pixel;
- }
-
- assert( pp == prototype.pixels + prototype.npixels );
- assert( tp == pixels + npixels );
- }
-
- // Shrink the prototype twice in each
- // dimension. The image row and column
- // dimensions are assumed to be even numbers
- // Image is shrunk by replacing 2x2 square
- // by one pixel with an average intensity
- // over the square
- void IMAGE::_shrink(const IMAGE& prototype)
- {
- prototype.is_valid();
- if( (prototype.nrows & 1) || (prototype.ncols & 1) )
- _error("No of rows and columns in the image to shrink should both be even",
- (prototype.info(),0));
- allocate(prototype.nrows/2,prototype.ncols/2,prototype.bits_per_pixel);
-
- register GRAY * tp = pixels;
- register GRAY * pp = prototype.pixels;
- register int i,j;
-
- for(i=0; i<nrows; i++)
- { // Average row-by-row
- for(j=0; j<ncols; j++)
- *tp = *pp++, *tp++ += *pp++; // Sum of pairs of the prototype pixels
- tp -= ncols;
- for(j=0; j<ncols; j++, tp++, pp+=2)
- {
- register int av = *tp + pp[0] + pp[1]; // This is the aver of 4 pixels
- *tp = ( av & 2 ? (av >> 2) + 1 : av >> 2 ); // /4 with rounding
- }
- }
- assert( pp == prototype.pixels + prototype.npixels );
- assert( tp == pixels + npixels );
- }
-
-
- // Assign another to '*this' resizing
- // '*this' as necessary to fit exactly
- // Squeezing is done by subsampling (dropping
- // some image pixels), while stretching is done
- // by repeating pixels/scanlines
- // Note, the ratio of sizes of the images
- // can be *anything*
- IMAGE& IMAGE::coerce(const IMAGE& another)
- {
- // Make the width even
- // width = ( dest_rect.q_width() + 1 ) & (~1);
- // height = dest_rect.q_height();
- // assert( width > 0 && height > 0 );
-
- // Figure out how much to
- // sqeeze/stretch
- const float ratio_h = (float)ncols/another.ncols;
- const float ratio_v = (float)nrows/another.nrows;
-
- float curr_v = 0, curr_h; // Precise curr. loc. in window coord
- card dest_y = 0, dest_x; // approx (rounded-off) coordinates
- register GRAY * drp = pixels; // Scanline ptr for fitted image
- register const GRAY * spp = another.pixels; // Scanline of 'another' image
-
- // Note, dest_x,dest_y is incremented
- // by (inc_v,inc_h), but curr_v is
- // incremented by ratio_v, etc.
- // Draw horizontally (row-by-row)
- while( spp < another.pixels + another.npixels )
- {
- curr_v += ratio_v;
- const GRAY * const spp_end = spp + another.ncols; // end of scanline
-
- int inc_v = (int)curr_v - dest_y;
- if( inc_v == 0 ) // inc_v = 0 means this row of
- { // orig_image is to be skipped
- spp = spp_end;
- continue;
- }
- dest_y = (int)curr_v;
-
- curr_h = 0;
- dest_x = 0;
- register GRAY * dpp = drp; // Squeeze/stretch pixels in a scanline
- for(; spp < spp_end; spp++)
- {
- curr_h += ratio_h;
-
- int inc_h = (int)curr_h - dest_x;
- dest_x = (int)curr_h;
- while( inc_h-- > 0 )
- *dpp++ = *spp;
- }
- if( dpp == drp+ncols-1 ) // Can happen due to roundoff arithm
- *dpp++ = *(spp-1);
- assert( dpp == drp+ncols );
- // Stretching vertically involves
- // duplicating scanlines
- while( --inc_v > 0 )
- memcpy((void *)(drp+ncols),(void *)drp,ncols*sizeof(GRAY)),
- drp += ncols;
- drp += ncols;
- }
- if( drp == pixels + npixels-ncols ) // Duplicate the last scanline
- memcpy((void *)drp,(void *)(drp-ncols),ncols*sizeof(GRAY)), // in case
- drp += ncols; // of roundoff snag
-
- assert( drp == pixels + npixels );
-
- return *this;
- }
-
-
- // Verify the pixels have the value
- // that is expected
- void verify_pixel_value(const IMAGE& im, const GRAY val)
- {
- register int i,j;
- for(i=0; i<im.q_nrows(); i++)
- for(j=0; j<im.q_ncols(); j++)
- if( im(i,j) != val )
- _error("Pixel [%d,%d] has the value 0x%x different from expected 0x%x",
- i,j,im(i,j),val);
- }
-
-